package com.piece.file.controller;

import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.constant.PermissionConstants;
import com.piece.core.framework.support.dto.UserDetailDTO;
import com.piece.core.web.controller.FrameWorkController;
import com.piece.core.framework.annotation.permission.AclPermission;
import com.piece.core.framework.annotation.permission.SubmitPermission;
import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.framework.enums.FileType;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.bootstrap.TreeData;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.file.CompressUtil;
import com.piece.core.framework.util.file.FileUtil;
import com.piece.file.model.TbSysFile;
import com.piece.file.service.TbSysFileService;
import com.piece.file.client.FileClientFactory;
import com.piece.module.file.constants.FileExceptionConstants;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@RestController
@Api(tags = {"系统文件可视化管理"})
@RequestMapping("/api/file/manage")
public class FileManageController extends FrameWorkController {

    @Resource
    private TbSysFileService tbSysFileService;

    @Autowired
    private FileClientFactory fileClientFactory;

    @PostMapping("/table")
    @AclPermission(code = "file:manage:view")
    @ApiOperation(value = "查询文件分页数据")
    @ApiImplicitParam(value = "查询条件", dataTypeClass = QueryTable.class, type = "body")
    public AjaxResponse table() {
        QueryTable queryTable = initTable();
        DataTable dataTable = tbSysFileService.findPage(queryTable);
        return success(dataTable);
    }

    /**
     * 加载文件夹列表树
     */
    @PostMapping("/treeData")
    @AclPermission(code = "file:manage:view")
    @ApiOperation(value = "加载资源文件夹列表树")
    @ApiImplicitParam(value = "id", name = "文件路径")
    public List<TreeData> treeData(UserDetailDTO user) throws Exception {
        // 文件根路径
        String root = applicationProperties.getOptions().get(ApplicationConstants.STORAGE_ROOT_PATH);
        boolean admin = user.isPermitted(PermissionConstants.ADMIN_PERMISSION);
        boolean home = user.getAuthorities().stream().anyMatch(grantedAuthority -> grantedAuthority.getAuthority().matches(PermissionConstants.HOME_PERMISSION));
        String path = getRequest().getParameter("id");
        path = path == null ? root : path;
        List<TreeData> trees = new ArrayList<>();
        // 读取文件夹
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(path + File.separator));
        BasicFileAttributes attrs;
        for (Path pathObj : directoryStream) {
            if (pathObj.endsWith("Home")) {
                if (!admin && !home) {
                    continue;
                }
            } else if (pathObj.endsWith("Primary")) {
                if (!admin) {
                    continue;
                }
            }
            // 获取文件基本属性
            attrs = Files.readAttributes(pathObj, BasicFileAttributes.class);
            if (attrs.isDirectory()) {
                TreeData treeData = TreeData.builder()
                        .id(pathObj.getParent().toString() + File.separator + pathObj.getFileName().toString())
                        .pId(pathObj.getParent().toString())
                        .title(pathObj.getFileName().toString())
                        .name(pathObj.getFileName().toString())
                        .isParent(true).build();

                trees.add(treeData);
            }
        }
        directoryStream.close();
        return trees;
    }

    @GetMapping("/load")
    @ApiOperation(value = "加载文件表单数据")
    @ApiImplicitParam(name="id", value = "主键")
    public AjaxResponse load() {
        String fileId = Convert.toStr(getRequest().getParameter("fileId"), null);
        TbSysFile file = new TbSysFile();
        if (StringUtil.isNotEmpty(fileId)) {
            file = tbSysFileService.findById(fileId);
        }
        return success(file);
    }

    @PostMapping("/save")
    @ApiOperation(value = "保存文件表单数据")
    @ApiImplicitParam(value = "文件表单数据", dataTypeClass = TbSysFile.class, type = "body")
    public AjaxResponse save() {
        TbSysFile file = new TbSysFile();
        try {
            if (StringUtil.isNotEmpty(file.getFileId())) {
                tbSysFileService.insert(file);
            } else {
                tbSysFileService.update(file);
            }
            return success(file);
        } catch (Exception e) {
            return error(e.getMessage());
        }
    }

    @PostMapping("/fileTable")
    @AclPermission(code = "file:manage:view")
    @ApiOperation(value = "资源文件分页数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "文件夹路径"),
            @ApiImplicitParam(name = "page", value = "起始页"),
            @ApiImplicitParam(name = "limit", value = "每页数据量")
    })
    public AjaxResponse fileTable(UserDetailDTO user) throws Exception {
        String root = applicationProperties.getOptions().get(ApplicationConstants.STORAGE_ROOT_PATH);
        String pageIndex = getRequest().getParameter("page");
        String pageSize = getRequest().getParameter("limit");
        String path = getRequest().getParameter("id");
        path = FileUtil.getRootPath(path, root);
        boolean fileOnly = Convert.toBool(getRequest().getParameter("file_only"), true);
        boolean admin = user.isPermitted(PermissionConstants.ADD_PERMISSION);
        boolean home = user.getAuthorities().stream().anyMatch(grantedAuthority -> grantedAuthority.getAuthority().matches(PermissionConstants.HOME_PERMISSION));
        QueryTable queryTable = new QueryTable();
        queryTable.setCurrentPage(Convert.toInt(pageIndex, 1));
        queryTable.setLength(Convert.toInt(pageSize, 20));
        queryTable.setStart((queryTable.getCurrentPage() - 1) * queryTable.getLength());
        List<TbSysFile> fileList = new ArrayList<>();
        int recordsTotal = 0;
        if (StringUtil.isNotEmpty(path)) {
            // 读取文件夹
            DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(path + File.separator));
            //BasicFileAttributes attrs;
            int start = queryTable.getStart();
            int size = queryTable.getLength();
            int begin = 0, end = start + size - 1;
            String originalFilename, extName;
            File file;
            for (Path pathObj : directoryStream) {
                // 获取文件基本属性
                //attrs = Files.readAttributes(pathObj, BasicFileAttributes.class);
                if (pathObj.endsWith("Home")) {
                    if (!admin && !home) {
                        continue;
                    }
                } else if (pathObj.endsWith("Primary")) {
                    if (!admin) {
                        continue;
                    }
                }
                if (begin >= start && begin <= end) {
                    TbSysFile tbSysFile = new TbSysFile();
                    tbSysFile.setFileId(FileUtil.generateUniqFileName(getRequest()));

                    file = new File(pathObj.toAbsolutePath().toString());
                    originalFilename = file.getName();
                    if (!file.isDirectory()) {
                        tbSysFile.setDir(false);
                        extName = FileUtil.getFileSuffix(pathObj.getFileName().toString()).toLowerCase();
                        tbSysFile.setContentType(extName);
                    } else if(fileOnly) {
                        continue;
                    } else {
                        tbSysFile.setDir(true);
                    }
                    tbSysFile.setOriginalFilename(originalFilename);
                    tbSysFile.setLocalPath(file.getAbsolutePath());
                    tbSysFile.setFileSize(file.length());
                    Long lastModified = file.lastModified();
                    tbSysFile.setUploadTime(new Date(lastModified));

                    if (FileUtil.checkImageType(originalFilename)) {
                        tbSysFile.setChannel(FileType.IMAGE.ordinal() + "");
                    } else if (FileUtil.checkOfficeType(originalFilename) || FileUtil.checkPdfType(originalFilename)) {
                        tbSysFile.setChannel(FileType.DOCUMENT.ordinal() + "");
                    } else if (FileUtil.checkAudioType(originalFilename)) {
                        tbSysFile.setChannel(FileType.MUSIC.ordinal() + "");
                    } else if (FileUtil.checkVideoType(originalFilename)) {
                        tbSysFile.setChannel(FileType.VIDEO.ordinal() + "");
                    } else {
                        tbSysFile.setChannel(FileType.OTHER.ordinal() + "");
                    }
                    fileList.add(tbSysFile);
                }
                begin++;
                recordsTotal++;
            }
            directoryStream.close();
        }

        DataTable dataTable = new DataTable(queryTable);
        dataTable.setRecordsTotal(recordsTotal);
        dataTable.setData(fileList);
        return success(dataTable);
    }

    @SubmitPermission
    @RequestMapping(value = "/create")
    @AclPermission(code = "file:manage:add")
    @ApiOperation(value = "创建文件夹")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "key", value = "文件路径"),
            @ApiImplicitParam(name = "name", value = "路径名称")
    })
    public AjaxResponse create(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 文件根路径
            String root = applicationProperties.getOptions().get(ApplicationConstants.STORAGE_ROOT_PATH);
            String path = request.getParameter("key");
            String name = request.getParameter("name");
            path = FileUtil.getRootPath(path, root);
            FileUtil.makeDir(path + File.separator + name);
            return success();
        } catch (Exception e) {
            log.error("删除文件失败", e);
            return error();
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/move")
    @AclPermission(code = "file:manage:move")
    @ApiOperation(value = "移动资源文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "newFilePath", value = "新文件路径"),
            @ApiImplicitParam(name = "files", value = "文件信息")
    })
    public AjaxResponse move() {
        try {
            String newFilePath = getRequest().getParameter("newFilePath");
            String filesJson = getRequest().getParameter("files");
            List<TbSysFile> files = JsonUtil.toList(filesJson, TbSysFile.class);
            files.forEach(file -> {
                File tempFile = new File(file.getLocalPath());
                if (tempFile.exists()) {
                    tempFile.renameTo(new File(newFilePath + File.separator + tempFile.getName()));
                }
            });
            return success();
        } catch (Exception e) {
            log.error("文件移动异常", e);
            return error();
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/size")
    @AclPermission(code = "file:manage:view")
    @ApiOperation(value = "获取资源文件大小")
    @ApiImplicitParam(name = "key", value = "文件路径")
    public AjaxResponse size() {
        try {
            // 文件根路径
            String root = applicationProperties.getOptions().get(ApplicationConstants.STORAGE_ROOT_PATH);
            String path = getRequest().getParameter("key");
            path = FileUtil.getRootPath(path, root);
            File file = new File(path);
            if (file.exists()) {
                return success(file.getTotalSpace());
            }
            return success(0);
        } catch (Exception e) {
            log.error("获取文件大小异常", e);
            return error();
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/upload")
    @AclPermission(code = "file:manage:upload")
    @ApiOperation(value = "资源文件上传")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "文件路径"),
            @ApiImplicitParam(name = "file", value = "上传文件", paramType = "file")
    })
    public AjaxResponse uploadFile(@RequestParam(value = "file", required = false) MultipartFile[] file) {
        try {
            String path = Convert.toStr(getRequest().getParameter("path"), "");
            if (null != file && file.length > 0) {
                String fileName;
                File temp;
                for (int i = 0; i < file.length; i++) {
                    fileName = file[i].getOriginalFilename();
                    temp = new File(path + File.separator + fileName);
                    FileUtil.write(temp, file[i].getInputStream());
                }
                return success(I18nUtil.message(ExceptionConstants.UPLOAD_SUCCESS));
            }
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR), e.getMessage());
        }
        return error(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR));
    }

    @RequestMapping(value = "/template/upload")
    @AclPermission(code = "file:template:add")
    @ApiOperation(value = "上传模板文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件表单", paramType = "file"),
            @ApiImplicitParam(name = "id", value = "原文件ID"),
            @ApiImplicitParam(name = "table", value = "模板名称"),
            @ApiImplicitParam(name = "channel", value = "文件类别，模板(5)")
    })
    public AjaxResponse uploadTemplateFile(@RequestParam(value = "file", required = false) MultipartFile[] file, UserDetailDTO user) {
        try {
            String table = Convert.toStr(getRequest().getParameter("table"));
            String channel = Convert.toStr(getRequest().getParameter("channel"));
            String fileId = Convert.toStr(getRequest().getParameter("id"));
            if (file != null && file.length > 0) {
                TbSysFile tbSysFile = null;
                if (StringUtil.isNotEmpty(fileId)) {
                    tbSysFile = tbSysFileService.findById(fileId);
                    fileClientFactory.get().deleteFile(tbSysFile);
                }
                fileId = "";
                for (int i = 0; i < file.length; i++) {
                    tbSysFile = fileClientFactory.get().store(file[i], "template");
                    tbSysFile.setTable(table);
                    tbSysFile.setChannel(channel);
                    tbSysFile.setCreateBy(user.getId());
                    tbSysFileService.update(tbSysFile);
                    fileId += "," + tbSysFile.getFileId();
                }
                if (fileId.length() > 0) {
                    return success(I18nUtil.message(ExceptionConstants.UPLOAD_SUCCESS), fileId.substring(1));
                } else {
                    return success(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR));
                }
            }
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR), e.getMessage());
        }
        return error(I18nUtil.message(ExceptionConstants.UPLOAD_ERROR));
    }

    @SubmitPermission
    @RequestMapping(value = "/download")
    @ApiOperation(value = "资源文件下载")
    @ApiImplicitParam(name = "path", value = "文件路径")
    public void fileDownload(HttpServletRequest request, HttpServletResponse response) {
        try {
            String path = request.getParameter("path");
            if (null != path) {
                File file = new File(path);
                InputStream is = new FileInputStream(file);
                FileUtil.downloadForWeb(request, response, is, file.getName());
            }
        } catch (Exception e) {
            log.error("下载文件失败", e);
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/delete")
    @AclPermission(code = "file:manage:delete")
    @ApiOperation(value = "资源文件删除")
    @ApiImplicitParam(name = "path", value = "文件路径")
    public AjaxResponse delete() {
        try {
            String path = getRequest().getParameter("path");
            FileUtil.deleteFile(path);
            return success();
        } catch (Exception e) {
            log.error("删除文件失败", e);
            return error();
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/unzip")
    @AclPermission(code = "file:manage:move")
    @ApiOperation(value = "资源文件解压")
    @ApiImplicitParam(name = "path", value = "文件路径")
    public AjaxResponse unzip(HttpServletRequest request) {
        try {
            String pathAndName = request.getParameter("path");
            File file = new File(pathAndName);
            if (file.exists()) {
                String path = FileUtil.getFilePrefix(pathAndName);
                String ext = FileUtil.getFileSuffix(file.getName());
                if (ext.equalsIgnoreCase("zip")) {
                    CompressUtil.unZipFile(pathAndName, path, false);
                } else if (ext.equalsIgnoreCase("rar")) {
                    CompressUtil.unRarFile(pathAndName, path, false);
                } else {
                    return error(I18nUtil.message(FileExceptionConstants.FORMAT, ext));
                }
            }
            return success();
        } catch (Exception e) {
            log.error("解压文件失败", e);
            return error();
        }
    }
}
